/*
 * Copyright 2008 Google Inc.
 * Copyright 2008 Georgios J. Georgopoulos.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.encon.projecttracker.client;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.HeadElement;
import com.google.gwt.dom.client.LinkElement;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * A utility class that loads style sheets.
 */
public class StyleSheetLoader {
	/**
	 * A {@link Timer} that creates a small reference widget used to determine
	 * when a new style sheet has finished loading. The widget has a natural
	 * width of 0px, but when the style sheet is loaded, the width changes to
	 * 5px. The style sheet should contain a style definition that is passed
	 * into the constructor that defines a height and width greater than 0px.
	 */
	private static class StyleTesterTimer extends Timer {
		private final Command callback;
		private final Label refWidget;

		/**
		 * Create a new {@link StyleTesterTimer}.
		 * 
		 * @param refStyleName
		 *            the reference style name
		 * @param callback
		 *            the callback to execute when the style sheet loads
		 */
		public StyleTesterTimer(final String refStyleName, final Command callback) {
			this.callback = callback;

			// Create the reference Widget
			refWidget = new Label();
			refWidget.setStyleName(refStyleName);
			refWidget.getElement().getStyle().setProperty("position", "absolute"); //$NON-NLS-1$ //$NON-NLS-2$
			refWidget.getElement().getStyle().setProperty("visibility", "hidden"); //$NON-NLS-1$ //$NON-NLS-2$
			refWidget.getElement().getStyle().setProperty("display", "inline"); //$NON-NLS-1$ //$NON-NLS-2$
			refWidget.getElement().getStyle().setPropertyPx("padding", 0); //$NON-NLS-1$
			refWidget.getElement().getStyle().setPropertyPx("margin", 0); //$NON-NLS-1$
			refWidget.getElement().getStyle().setPropertyPx("border", 0); //$NON-NLS-1$
			refWidget.getElement().getStyle().setPropertyPx("top", 0); //$NON-NLS-1$
			refWidget.getElement().getStyle().setPropertyPx("left", 0); //$NON-NLS-1$
			RootPanel.get().add(refWidget);
		}

		@Override
		public void run() {
			// Redisplay the reference widget so it redraws itself
			refWidget.setVisible(false);
			refWidget.setVisible(true);

			// Check the dimensions of the reference widget
			if (refWidget.getOffsetWidth() > 0) {
				RootPanel.get().remove(refWidget);

				if (callback != null) {
					// Fire the callback in a DeferredCommand to ensure the
					// browser has
					// enough time to parse the styles. Otherwise, we'll get
					// weird styling
					// issues.
					DeferredCommand.addCommand(callback);
				}
			} else {
				schedule(10);
			}
		}
	}

	/**
	 * Convenience method for getting the document's head element.
	 * 
	 * @return the document's head element
	 */
	public static native HeadElement getHeadElement()
	/*-{
	  return $doc.getElementsByTagName("head")[0];
	}-*/;

	/**
	 * Load a style sheet onto the page.
	 * 
	 * @param href
	 *            the url of the style sheet
	 */
	public static void loadStyleSheet(final String href) {
		final LinkElement linkElem = Document.get().createLinkElement();
		linkElem.setRel("stylesheet"); //$NON-NLS-1$
		linkElem.setType("text/css"); //$NON-NLS-1$
		linkElem.setHref(href);
		getHeadElement().appendChild(linkElem);
	}

	/**
	 * Load a style sheet onto the page and fire a callback when it has loaded.
	 * The style sheet should contain a style definition called refStyleName
	 * that defines a height and width greater than 0px.
	 * 
	 * @param href
	 *            the url of the style sheet
	 * @param refStyleName
	 *            the style name of the reference element
	 * @param callback
	 *            the callback executed when the style sheet has loaded
	 */
	public static void loadStyleSheet(final String href, final String refStyleName, final Command callback) {
		loadStyleSheet(href);
		waitForStyleSheet(refStyleName, callback);
	}

	/**
	 * Detect when a style sheet has loaded by placing an element on the page
	 * that is affected by a rule in the style sheet, as described in
	 * {@link #loadStyleSheet(String, String, Command)}. When the style sheet
	 * has loaded, the callback will be executed.
	 * 
	 * @param refStyleName
	 *            the style name of the reference element
	 * @param callback
	 *            the callback executed when the style sheet has loaded
	 */
	public static void waitForStyleSheet(final String refStyleName, final Command callback) {
		new StyleTesterTimer(refStyleName, callback).run();
	}
}
